﻿namespace WP7Contrib.Caching.Tests
{
    using System;
    using System.Collections.Generic;
    using System.Device.Location;
    using System.Linq;
    using System.Reflection;
    using Microsoft.Phone.Controls.Maps;
    using Microsoft.Silverlight.Testing;
    using Microsoft.VisualStudio.TestTools.UnitTesting;
    using Models;

    /// <summary>
    /// Null cache provider unit tests - we follow BDD principles, http://dannorth.net/introducing-bdd/
    /// </summary>
    [TestClass]
    public class IsolatedStorageCacheProviderTests : SilverlightTest
    {
        [TestMethod]
        public void ShouldThrowExceptionWhenExpiryIsAYearInTheFuture()
        {
            // Given we have an isolated storage cache provider...
            var cacheProvider = new IsolatedStorageCacheProvider("IsolatedStorageCacheProviderTests", Enumerable.Empty<Assembly>());

            Exception exception = null;
            try
            {
                // When we add a value to the cache and set the expiry to a year in the future...
                cacheProvider.Add("Test_01", "Value_01", DateTime.Now.AddYears(1));
            }
            catch (Exception exn)
            {
                exception = exn;
            }

            // Then we expect and exception to have been generated...
            Assert.IsNotNull(exception);
            Assert.IsInstanceOfType(exception, typeof(ArgumentException));
        }

        [TestMethod]
        public void ShouldNotThrowExceptionWhenExpiryIsUInt32MaxMinusDividedBy2()
        {
            // Given we have an isolated storage cache provider...
            var cacheProvider = new IsolatedStorageCacheProvider("IsolatedStorageCacheProviderTests", Enumerable.Empty<Assembly>());

            // Given we have the number of milliseconds required...
            var timeSpan = TimeSpan.FromMilliseconds(UInt32.MaxValue / 2);

            // When we add a value to the cache...
            cacheProvider.Add("Test_01", "Value_01", timeSpan);

            // Then we do not expect an exception...
        }

        [TestMethod]
        public void ShouldThrowExceptionWhenExpiryIsUInt32Max()
        {
            // Given we have an isolated storage cache provider...
            var cacheProvider = new IsolatedStorageCacheProvider("IsolatedStorageCacheProviderTests", Enumerable.Empty<Assembly>());

            // Given we have the number of milliseconds required...
            var timeSpan = TimeSpan.FromMilliseconds(UInt32.MaxValue);

            Exception exception = null;
            try
            {
                // When we add a value to the cache...
                cacheProvider.Add("Test_01", "Value_01", timeSpan);
            }
            catch (Exception exn)
            {
                exception = exn;
            }

            // Then we expect and exception to have been generated...
            Assert.IsNotNull(exception);
            Assert.IsInstanceOfType(exception, typeof(ArgumentException));
        }
        
        [TestMethod]
        public void ShouldBeAbleToCacheSimpleModel()
        {
            // Given we have an isolated storage cache provider...
            var cacheProvider = new IsolatedStorageCacheProvider("IsolatedStorageCacheProviderTests", Enumerable.Empty<Assembly>());

            // Given we have a model we want to check for cacheability...
            var model = new SuccessfulSimpleModel {FirstName = "ollie", LastName = "riches"};

            // When we test for cacheability...
            IEnumerable<Type> failingTypes = null;
            var cacheable = cacheProvider.IsCacheable(model, ref failingTypes);

            //Then we expect it to be cacheable...
            Assert.IsTrue(cacheable);
            Assert.IsFalse(failingTypes.Any());
        }

        [TestMethod]
        public void ShouldBeAbleToCacheComplexModel()
        {
            // Given we have an isolated storage cache provider...
            var cacheProvider = new IsolatedStorageCacheProvider("IsolatedStorageCacheProviderTests", Enumerable.Empty<Assembly>());

            // Given we have a model we want to check for cacheability...
            var model = new SuccessfulComplexModel
                            {
                                FirstName = "ollie",
                                LastName = "riches",
                                Location = new GeoCoordinate(51.554111, -0.072784)
                            };
            model.Area = new LocationRect(model.Location, 10, 10);
            model.Stuff.Add("Stuff1");
            model.Stuff.Add("Stuff2");
            model.Stuff.Add("Stuff3");
            
            // When we test for cacheability...
            IEnumerable<Type> failingTypes = null;
            var cacheable = cacheProvider.IsCacheable(model, ref failingTypes);

            //Then we expect it to be cacheable...
            Assert.IsTrue(cacheable);
            Assert.IsFalse(failingTypes.Any());
        }

        [TestMethod]
        public void ShouldBeAbleToCacheModelEvenWithDoNotSerializeAttrbiutes()
        {
            // Given we have an isolated storage cache provider...
            var cacheProvider = new IsolatedStorageCacheProvider("IsolatedStorageCacheProviderTests", Enumerable.Empty<Assembly>());

            // Given we have a model we want to check for cacheability...
            var model = new SuccessfulComplexModelWithDoNotSerializeAttribute()
            {
                FirstName = "ollie",
                LastName = "riches",
                Location = new GeoCoordinate(51.554111, -0.072784)
            };
            model.Area = new LocationRect(model.Location, 10, 10);
            model.Stuff.Add("Stuff1");
            model.Stuff.Add("Stuff2");
            model.Stuff.Add("Stuff3");

            // When we test for cacheability...
            IEnumerable<Type> failingTypes = null;
            var cacheable = cacheProvider.IsCacheable(model, ref failingTypes);

            //Then we expect it to be cacheable...
            Assert.IsTrue(cacheable);
            Assert.IsFalse(failingTypes.Any());
        }

        [TestMethod]
        public void ShouldNotBeAbleToCacheComplexModel()
        {
            // Given we have an isolated storage cache provider...
            var cacheProvider = new IsolatedStorageCacheProvider("IsolatedStorageCacheProviderTests", Enumerable.Empty<Assembly>());

            // Given we have a model we want to check for cacheability...
            var model = new FailureComplexModel()
            {
                FirstName = "ollie",
                LastName = "riches",
                Location = new GeoCoordinate(51.554111, -0.072784)
            };
            model.Area = new LocationRect(model.Location, 10, 10);
            model.Url = new Uri("http://www.google.com");
            
            // When we test for cacheability...
            IEnumerable<Type> failingTypes = null;
            var cacheable = cacheProvider.IsCacheable(model, ref failingTypes);

            //Then we expect it not to be cacheable...
            Assert.IsFalse(cacheable);
            Assert.IsTrue(failingTypes.Contains(typeof(Uri)));
        }

        [TestMethod]
        public void ShouldNotBeAbleToCacheNestedComplexModel()
        {
            // Given we have an isolated storage cache provider...
            var cacheProvider = new IsolatedStorageCacheProvider("IsolatedStorageCacheProviderTests", Enumerable.Empty<Assembly>());

            // Given we have a model we want to check for cacheability...
            var model = new FailureNestedComplexModel()
            {
                Field1 = "ollie",
                Field2 = "riches",
                Nested = new FailureComplexModel{ Url = new Uri("http://www.bing.com")},
                Nested2 = new SuccessfulComplexModel{ FirstName = "ollie", LastName = "riches", Location = new GeoCoordinate(51.554111, -0.072784) } 
            };

            model.Nested2.Area = new LocationRect(model.Nested2.Location, 10, 10);
            model.Nested2.Stuff.Add("Stuff1");
            model.Nested2.Stuff.Add("Stuff2");
            model.Nested2.Stuff.Add("Stuff3");
            model.Nested2.Stuff2.Add("Stuff4");
            model.Nested2.Stuff2.Add("Stuff5");
            model.Nested2.Stuff2.Add("Stuff6");

            // When we test for cacheability...
            IEnumerable<Type> failingTypes = null;
            var cacheable = cacheProvider.IsCacheable(model, ref failingTypes);

            //Then we expect it not to be cacheable...
            Assert.IsFalse(cacheable);
            Assert.IsTrue(failingTypes.Contains(typeof(Uri)));
        }
    }
}
